# -*- Mode: makefile -*-
# Copyright (c) 2001-2002, 
#  George C. Necula    <necula@cs.berkeley.edu>
#  Scott McPeak        <smcpeak@cs.berkeley.edu>
#  Wes Weimer          <weimer@cs.berkeley.edu>
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. The names of the contributors may not be used to endorse or promote
# products derived from this software without specific prior written
# permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

  # Generic Makefile for Ocaml projects
  # Written by necula@cs.berkeley.edu
  # 
  # Features: 
  #   - keeps byproducts of building in a separate directory
  #   - handles dependencies automatically
  #   - user specifies just what modules go into a project and 
  #     everything else is done automatically
  #   - you can use one Makefile for several Ocaml projects
  #   
  # You must include this file in your Makefile. Before the include point 
  # you must defined the following variables (which are glob al for all Ocaml 
  # projects specified in one Makefile):
  # 
  # CAMLDIR    - the directory where to get the ocaml executables from. 
  #              Must be empty (defaul) or end with a /
  # OBJDIR     - the directory where to put all object files. This directory 
  #              must exist (default obj)
  # DEPENDDIR  - the directory where to put dependency files. This directory 
  #              must exist.  (default obj/.depend)
  # NATIVECAML - if set then will use the native compiler
  # UNSAFE     - if set then will turn off safety checks (only with NATIVECAML)
  # PROFILE    - if set then it will compile and link with "gprof" profiling
  #              support (NATIVECAML mode only)
  # ASSEMBLY   - if set then it will keep assembly files
  # STATIC     - if set then it will compile and link statically
  #              (NATIVECAML mode only)
  # PREPROC    - the preprocessor command

  # MODULES    - a list of all modules for all projects defined in the 
  #              Makefile. Give only the basenames (no directory, 
  #              no extension).  This is used to create the dependencies.
  # SOURCEDIRS - a list of all directories containing sources for all 
  #              projects defined in a Makefile. This is used to set vpath.
  # MLLS       - a list of all .mll (ocamllex input) files for all 
  #              projects defined in the Makefile. 
  # MLYS       - a list of all .mly (ocamlyacc input) files for all 
  #              projects defined in the Makefile. 
  # ECHO       - if specifically set to nothing then it will print 
  #              all of the commands issued. Set this in the command line
  #              if you want to see what is going on.
  #
  # COMPILEFLAGS      - if defined, then it is passed as argument to ocamlc
  #                     and ocamlopt
  # LINKFLAGS         - if defined, then it is passed as argument to 
  #                     ocamlc and ocamlopt, when linking (at start of 
  #                     command line)
  #
  # CAML_CFLAGS   - flags used only for the compilation of C files.
  #                 e.g. '-ccopt <gcc flag>'
  #
  # BEFOREDEPS  - must list the ML files that must be created before computing 
  #               dependencies. If you do not do this, then ocamldep will not
  #               record a dependency on the missing ML files. 
  #               The files obtained from MLYS and MLLS are 
  #               handled automatically. This must be set before including this
  #               Makefile.ocaml
  # 
  # After you set all of the above you must do the following for EACH separate 
  # executable that you want to build.
  #
  # Define the following:
  # PROJECT_EXECUTABLE - the name of the executable you want to build. To take 
  #                      advantage of the naming scheme that separates the 
  #                      bytecode version and the native version, use the 
  #                      $(EXE) variable which is defined to either .byte.exe 
  #                      or .asm.exe. I typically put the executable in 
  #                      $(OBJDIR) as well.
  # PROJECT_MODULES    - the base names of the modules that make this 
  #                      executable in the order in which they must be
  #                      passed to the linker. Make sure that all of
  #                      the names mentioned here are also mentioned in 
  #                      MODULES.
  # PROJECT_CMODULES   - same as modules but for the C modules. These
  #                      do not need to be mentioned in MODULES. There must be
  #                      no name clashes with MODULES
  # PROJECT_LIBS       - the base names of the libraries that you 
  #                      want to link in the executable.
  #
  #
  # Then include Makefile.ocaml.build to generate a customized
  # rule for making your executable.
  #
  # Example:
  # 
  # OBJDIR     = obj
  # DEPENDDIR  = obj/.depend
  # SOURCEDIRS = src src/special
  # MLLS       = mylex 
  # MLYS       = myparse
  #
  # MODULES    = mod11 mod12 mod21 modcommon
  #
  #  # Rules for project 1
  # PROJECT_EXECUTABLE = $(OBJDIR)/proj1$(EXE)
  # PROJECT_MODULES    = mod11 mod12 modcommon
  # PROJECT_CMODULES   =  
  # PROJEC_LIBS        = unix
  # include Makefile.ocaml.build
  #
  #
  #  # Rules for project 2 
  # PROJECT_EXECUTABLE = $(OBJDIR)/proj2$(EXE)
  # PROJECT_MODULES    = mod21 modcommon
  # PROJECT_CMODULES   =  
  # PROJEC_LIBS        = unix str
  # include Makefile.ocaml.build


CAMLLEX = ocamllex
CAMLYACC= ocamlyacc -v
CAMLDEP = ocamldep

COMPILEFLAGS += -I $(OBJDIR)

# sm: two styles for echoing compilation progress:
#   style 1, by George:
#     - print English descriptions of what's happening
#     - set ECHO to "" to see *everything*
#   style 2, by Scott:
#     - do not print English descriptions
#     - print every shell command that is executed which has a side effect,
#       so that they could be pasted into a shell to reproduce manually
#     - omit some of the details of dependency generation
#
# to be able to choose which style, several variables are used:
#   @$(NARRATIVE)  - put this before English descriptions for style 1
#   @$(COMMAND)    - put this before shell commands which are to be
#                    printed for style 2; the command is *not* executed
#   $(AT)          - put this before shell commands which are to be executed,
#                    and also printed in style 2
#   $(ECHO)        - use in place of '@' for things not printed in either style
ifdef ECHOSTYLE_SCOTT
  # 'true' silently consumes its arguments, whereas 'echo' prints them
  NARRATIVE   := true           
  COMMAND     := echo
  AT          := 
  ECHO        := @
else
  NARRATIVE   := echo
  COMMAND     := true
  # change these next two definitions to <empty> to echo everything,
  # or leave as @ to suppress echoing
  AT          := @
  ECHO        := @
endif

ifdef PREPROC
  COMPILEFLAGS += -pp "$(PREPROC)$"
  DEPFLAGS += -pp "$(PREPROC)"
endif

COMPILEMSG=
LINKMSG=

ifdef WIN32
OBJ             = obj
else
OBJ             = o
endif
EXE		= $(EXEEXT).exe


export EXE

ifdef NATIVECAML
 ifdef PROFILE
   COMPILEFLAGS   += -p
   LINKFLAGS      += -p
   COMPILEMSG     += (profile)
   LINKMSG        += (profile)
 endif
 ifdef ASSEMBLY
   COMPILEFLAGS   += -S
 endif
 ifdef STATIC
   COMPILEFLAGS   += -ccopt -static
   LINKFLAGS      += -ccopt -static
 endif
 #foo := $(shell echo "I am in NATIVECAML mode" >&2; echo whatever)
 ifdef WIN32
   COMPILEFLAGS    += -ccopt /Ox
 else
   COMPILEFLAGS    += -ccopt -O3
 endif
 CAMLC          = $(CAMLDIR)ocamlopt  $(COMPILEFLAGS)
 CAMLLINK       = $(CAMLDIR)ocamlopt  $(LINKFLAGS)
 CMO            = cmx
 CMC            = opt.$(OBJ)  # compiled (and optimized) C
 CMXA           = cmxa
 EXEEXT         = .asm
 MOVEAFTERCAMLC = cmi cmx $(OBJ)
 COMPILETOWHAT  = native code
 # sm: by adding -native in native mode, we prevent spurious
 # dependencies on .cmo files which were causing lots of
 # extra recompilation
 CAMLDEP        = $(CAMLDIR)ocamldep -native
else 
 CMO            = cmo
 CMXA           = cma
 CMC            = $(OBJ)
 EXEEXT         = .byte
 MOVEAFTERCAMLC = cmi cmo
 COMPILETOWHAT  = bytecode
 ifdef WIN32
   COMPILEFLAGS += -ccopt /Zi -ccopt /Od
   LINKFLAGS    += -ccopt /Zi -ccopt /Od
 else
   COMPILEFLAGS += -g -ccopt -g
   LINKFLAGS    += -g -ccopt -g
 endif
 CAMLC          = $(CAMLDIR)ocamlc -g $(COMPILEFLAGS)
 CAMLLINK       = $(CAMLDIR)ocamlc -custom  $(LINKFLAGS)
endif


ifdef UNSAFE 
 CAMLC          := $(CAMLC) -unsafe -noassert
endif




    # Allow searching for .ml and .mli
vpath %.mll $(SOURCEDIRS)
vpath %.mly $(SOURCEDIRS)
vpath %.ml  $(SOURCEDIRS) $(OBJDIR)
vpath %.mli $(SOURCEDIRS) $(OBJDIR)
vpath %.c   $(SOURCEDIRS)


#  Secondaries are intermediates that we don't want make to delete
#  By giving the right names to secondary files we tell make where to make
#  them if they are not already made. VERY USEFUL!!

MLL_LYS:= $(MLLS:%.mll=$(OBJDIR)/%.ml)  \
          $(MLYS:%.mly=$(OBJDIR)/%.ml) $(MLYS:%.mly=$(OBJDIR)/%.mli)

.SECONDARY : $(MLL_LYS)

             # Run the lexer generator
             # Move the result to the OBJDIR directory
             # If there is a .mli file in the same directory with .mll then
             # copy it to OBJDIR (where the .ml) file will live.
$(OBJDIR)/%.ml: %.mll
	$(CAMLLEX) $<
	$(AT)mv -f $(basename $<).ml $(OBJDIR)/
	$(ECHO)if test -f $(basename $<).mli ;then \
	  $(COMMAND) cp -f $(basename $<).mli $(OBJDIR)/; \
	  cp -f $(basename $<).mli $(OBJDIR)/ \
        ;fi

             # Run the parser generator
             # Move the result to the $(OBJDIR) directory.
$(OBJDIR)/%.ml $(OBJDIR)/%.mli: %.mly
	$(CAMLYACC) $(CAMLYACCFLAGS) $<
	$(AT)mv -f $(basename $<).ml $(basename $<).mli $(OBJDIR)/

         # Compile an MLI file. After compilation move the result to OBJDIR
$(OBJDIR)/%.cmi: %.mli
	@$(NARRATIVE) Compiling interface $<
	$(AT)$(CAMLC) $(COMPILEFLAGS) -c $<
	$(ECHO)if test $(OBJDIR) != $(<D) ;then \
                   $(COMMAND) mv -f $(basename $<).cmi $(OBJDIR)/; \
                   mv -f $(basename $<).cmi $(OBJDIR)/ \
        ;fi

         # Compile an ML file. After compilation we 
         # copy to $(OBJDIR) the .cmi and the result of compilation.
$(OBJDIR)/%.$(CMO): %.ml
	@$(NARRATIVE) "Compiling           $< to $(COMPILETOWHAT) $(COMPILEMSG)"
#	$(ECHO)#if test $(OBJDIR) != $(<D) -a -f $(OBJDIR)/$(basename $(<F)).cmi ;then \
#           $(COMMAND) mv -f $(OBJDIR)/$(basename $(<F)).cmi $(<D); \
#           mv -f $(OBJDIR)/$(basename $(<F)).cmi $(<D); \
#        fi
	@$(COMMAND) $(CAMLC) $(COMPILEFLAGS) -c $<
	$(ECHO)$(CAMLC) $(COMPILEFLAGS) -c $< ; res=$$?; \
	   if test $(OBJDIR) != $(<D) ;then \
              for ext in $(MOVEAFTERCAMLC); do \
                if test -f $(basename $<).$$ext ;then \
                  $(COMMAND) mv -f $(basename $<).$$ext $(OBJDIR)/; \
                  mv -f $(basename $<).$$ext $(OBJDIR)/; \
                fi; \
              done; \
           fi; exit $$res

             # Compile C files
             # They appear to be left in the current directory as .o files
$(OBJDIR)/%.$(CMC): %.c
	@$(NARRATIVE) "Compiling C file $< $(COMPILEMSG)"
	$(AT)$(CAMLC) $(COMPILEFLAGS) $(CAML_CFLAGS) -c $< -o $@
	$(AT)mv -f $(basename $(notdir $<)).$(OBJ) $@

              # Special rule for profile.c
CAMLC_NOPROF=$(subst -p,,$(CAMLC))
$(OBJDIR)/profile.$(CMC): profile.c
	@$(NARRATIVE) "Compiling C file $<"
	$(AT)$(CAMLC_NOPROF) $(COMPILEFLAGS) $(CAML_CFLAGS) -c $< -o $@
	$(AT)mv -f $(basename $(notdir $<)).$(OBJ) $@


#	Phonies should be "remade" even if someone mistakenly creates them
.PHONY: cleancaml
cleancaml:
	-rm -f $(OBJDIR)/*.cmi
	-rm -f $(OBJDIR)/*.cmo
	-rm -f $(OBJDIR)/*.cmx
	-rm -f $(OBJDIR)/*.cma
	-rm -f $(OBJDIR)/*.cmxa
	-rm -f $(OBJDIR)/*.exe
	-rm -f $(OBJDIR)/*.obj
	-rm -f $(OBJDIR)/*.o
	-rm -f $(OBJDIR)/*.obj
	-rm -f $(OBJDIR)/*.o
	-rm -f $(OBJDIR)/*.lib
	-rm -f $(OBJDIR)/*.a
	-rm -f $(OBJDIR)/*.mli
	-rm -f $(OBJDIR)/*.ml
	-rm -f $(DEPENDDIR)/*.d $(DEPENDDIR)/*.di
	-rm -f $(MLLS:%.mll=$(OBJDIR)/%.ml) \
               $(MLLS:%.mll=$(OBJDIR)/%.mli) \
               $(MLYS:%.mly=$(OBJDIR)/%.ml) \
               $(MLYS:%.mly=$(OBJDIR)/%.mli)


# Before we generate the dependencies, we must make sure to create all the 
# ML files that we need. Otherwise, the ocamldep will not point out a 
# dependency to a missing file
BEFOREDEPS += $(MLLS:%.mll=$(OBJDIR)/%.ml) $(MLYS:%.mly=$(OBJDIR)/%.ml)

# Automatic dependency generation (see GNU info for details)
#
# Each .ml file has a .d (dependency file) which is automatically
# generated and included by the rules below.  The perl script replaces
# directory paths with $(OBJDIR)/
#
# Dependencies for .mli files reside in corresponding .di files.
#

# Replace the directories in the dependency rules with $(OBJDIR)/, since 
# we'll move .cmo/.cmx files there.
# 1. Strip any text followed by / or \.  The / case even strips slashes that
#    are preceded by whitespace, to account for unix absolute paths.
#    The \ case does not strip slashes that come immediately after whitespace,
#    to preserve the trailing \ at the end of Makefile rules.
# 2. Replace these directory names by '$(OBJDIR)/'
FIXDEPEND:=perl -e 'while(<>) { s%[^/\\ :]*/% %g; s%[^/\\ :]+\\% %g; s%([-a-zA-Z0-9+-.:/\/_]+)%\$$(OBJDIR)/$$1%g; print $$_;}'
# FIXDEPEND:=cat

DEPINCLUDES= -I $(OBJDIR) $(SOURCEDIRS:%=-I %)
$(DEPENDDIR)/%.d: %.ml $(BEFOREDEPS)
	@$(NARRATIVE) "Generating dependency information for $<"
	$(ECHO)if ! [ -d $(DEPENDDIR) ]; then mkdir -p $(DEPENDDIR) ; fi
	@$(COMMAND) $(CAMLDEP) $(DEPFLAGS) $(DEPINCLUDES) $<
	$(ECHO)$(CAMLDEP) $(DEPFLAGS) $(DEPINCLUDES) $< | $(FIXDEPEND) > $@

$(DEPENDDIR)/%.di: %.mli $(BEFOREDEPS)
	@$(NARRATIVE) "Generating dependency information for $<"
	$(ECHO)if ! [ -d $(DEPENDDIR) ]; then mkdir -p $(DEPENDDIR) ; fi
	@$(COMMAND) $(CAMLDEP) $(DEPFLAGS) $(DEPINCLUDES) $<
	$(ECHO)$(CAMLDEP) $(DEPFLAGS) $(DEPINCLUDES) $< | $(FIXDEPEND) > $@

# sm: it turns out there's a variable which lists all the goals
# specified on the command line; I'll use this to set CLEANING
# (which is not set anywhere else, currently)
ifeq ($(MAKECMDGOALS),clean)
  #$(warning "Skipping dependency rules because we're cleaning")
  CLEANING := 1
endif

ifndef CLEANING
-include $(MODULES:%=$(DEPENDDIR)/%.d)
-include $(MODULES:%=$(DEPENDDIR)/%.di)
endif

listmodules: 
	@echo $(MODULES)
